﻿' ------------------------------------------------------------------------------
' Image handling utility methods for
'
'   - mapping filenames to EnumImageType
'   - resizing images to thumbnails
'   - resizing width/height while preserving aspect ratio
'   - conversion from/to byte arrays, BitmapImage objects, SqlFilestream objects
'
' ------------------------------------------------------------------------------
' This file is part of the SQL Server 2008 Technology Demonstration. This
' application was designed and coded by Eric Weitzman, LandTime Inc, for
' Microsoft Corporation. Contact Ed Katibah at Microsoft about this demo.
' Copyright © 2008 Microsoft Corporation. All rights reserved.
' ------------------------------------------------------------------------------

Imports System.Windows.Media.Imaging
Imports SpatialAlbum.Core.Enums.EnumImageType

Public Module ImageUtils

    Public Structure Size ' avoid referencing System.Drawing
        Public Width As Integer
        Public Height As Integer
        Public Sub New(ByVal w As Integer, ByVal h As Integer)
            Width = w
            Height = h
        End Sub
    End Structure

    Public Const THUMBNAIL_SIZE As Integer = 96
    Public Const TINY_THUMBNAIL_SIZE As Integer = 24

    Public Function ImageType(ByVal fname As String) As EnumImageType
        Select Case IO.Path.GetExtension(fname).ToLower
            Case ".bmp" : Return eBmp
            Case ".gif" : Return eGif
            Case ".jpg", ".jpeg" : Return eJpg
            Case ".png" : Return ePng
            Case ".tif", ".tiff" : Return eTif
            Case ".wmp" : Return eWmp
            Case Else : Return eNone
        End Select
    End Function

    Public Function GetThumbnail(ByVal image As BitmapImage) As BitmapImage
        Dim thumbnail As BitmapImage = Nothing
        Try
            Dim imagebytes() As Byte = ConvertImageToBytes(image, eJpg)
            Dim newsize As Size = ShrinkAspect(image.PixelWidth, image.PixelHeight, THUMBNAIL_SIZE, THUMBNAIL_SIZE)
            thumbnail = ConvertBytesToImage(imagebytes, newsize.Width, newsize.Height)
        Catch ex As Exception
            ReportException(ex)
        End Try
        Return thumbnail
    End Function

    Public Function GetTinyThumbnail(ByVal image As BitmapImage) As BitmapImage
        Dim thumbnail As BitmapImage = Nothing
        Try
            Dim imagebytes() As Byte = ConvertImageToBytes(image, eJpg)
            Dim newsize As Size = ShrinkAspect(image.PixelWidth, image.PixelHeight, TINY_THUMBNAIL_SIZE, TINY_THUMBNAIL_SIZE)
            thumbnail = ConvertBytesToImage(imagebytes, newsize.Width, newsize.Height)
        Catch ex As Exception
            ReportException(ex)
        End Try
        Return thumbnail
    End Function

    Public Function LoadImageFromFilestream(ByVal guid As Guid, ByVal table As String, ByVal column As String, ByVal connection As Connection) As BitmapImage
        Try
            Dim b() As Byte = SqlFilestream.LoadBytes(guid, table, column, connection)
            Return ConvertBytesToImage(b, 0, 0)
        Catch
            Return Nothing
        End Try
    End Function

    Public Function LoadImageBytesFromFile(ByVal filePath As String) As Byte()
        Dim fs As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read)
        Dim br As New IO.BinaryReader(fs)
        Dim b() As Byte = br.ReadBytes(CType(fs.Length, Integer))
        br.Close()
        Return b
    End Function

    Public Sub SaveImageBytesToFile(ByVal imagedata As Byte(), ByVal filePath As String)
        Dim fs As New IO.FileStream(filePath, IO.FileMode.CreateNew, IO.FileAccess.Write)
        Dim bw As New IO.BinaryWriter(fs)
        bw.Write(imagedata, 0, imagedata.Length)
        bw.Close()
    End Sub

    Public Function ConvertBytesToImage(ByVal bytes() As Byte, Optional ByVal width As Integer = 0, Optional ByVal height As Integer = 0) As BitmapImage
        Dim image As New BitmapImage
        image.BeginInit()
        If width > 0 Then image.DecodePixelWidth = width
        If height > 0 Then image.DecodePixelHeight = height
        image.StreamSource = New IO.MemoryStream(bytes)
        image.CreateOptions = BitmapCreateOptions.None
        image.CacheOption = BitmapCacheOption.Default
        image.EndInit()
        Return image
    End Function

    Public Function ConvertImageToBytes(ByVal image As BitmapSource, ByVal itype As EnumImageType) As Byte()

        Dim encoder As BitmapEncoder = Nothing
        Select Case itype
            Case eBmp : encoder = New BmpBitmapEncoder
            Case eGif : encoder = New GifBitmapEncoder
            Case eJpg : encoder = New JpegBitmapEncoder
            Case ePng : encoder = New PngBitmapEncoder
            Case eTif : encoder = New TiffBitmapEncoder
            Case eWmp : encoder = New WmpBitmapEncoder
            Case Else : AssertNotReached("ConvertImageToBytes")
        End Select

        ' encode image to a memory stream
        encoder.Frames.Add(BitmapFrame.Create(image))
        Dim memstr As New IO.MemoryStream
        encoder.Save(memstr)

        Dim len As Integer = CType(memstr.Length, Integer)
        Dim bytes(len) As Byte

        ' copy data from memory stream to a byte array
        memstr.Seek(0, IO.SeekOrigin.Begin)
        Dim br As New IO.BinaryReader(memstr)
        br.Read(bytes, 0, len)
        br.Close()
        memstr.Close()

        Return bytes

    End Function

    ' Given the width and height (of an image) and target maximum width and height, returns a 
    ' new Size instance that fits within the target while preserving the original aspect ratio.
    '
    ' For example, a width/height of 1024/768 and target 16/16 will produce a size of 16/12.

    Public Function ShrinkAspect(ByVal width As Integer, ByVal height As Integer, ByVal maxwidth As Integer, ByVal maxheight As Integer, Optional ByVal reduction As Double = 1.0) As Size
        Dim w As Double = width
        Dim h As Double = height
        Dim mw As Double = maxwidth * reduction
        Dim mh As Double = maxheight * reduction

        Dim scale As Double = 1.0
        If w > mw Then scale = mw / w
        If h > mh Then scale = Math.Min(scale, mh / h)

        Return New Size(CInt(w * scale), CInt(h * scale))
    End Function

End Module
